﻿namespace AutoRevit.ThreadTask;

/// <summary>
/// 多任务并发调度器
/// </summary>
internal class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
    /// <summary>当前线程是否正在处理工作项目。</summary>
    [ThreadStatic]
    private static bool _currentThreadIsProcessingItems;

    /// <summary>要执行的任务列表。</summary>
    private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)

    /// <summary>该调度器允许的最大并发水平。</summary>
    private readonly int _maxDegreeOfParallelism;

    /// <summary>调度器当前是否正在处理工作项目。</summary>
    private int _delegatesQueuedOrRunning = 0; // protected by lock(_tasks)

    /// <summary>
    /// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the
    /// specified degree of parallelism.
    /// </summary>
    /// <param name="maxDegreeOfParallelism">The maximum degree of parallelism provided by this scheduler.</param>
    public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism = 1)
    {
        if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
        _maxDegreeOfParallelism = maxDegreeOfParallelism;
    }

    /// <summary>
    /// current executing number;
    /// </summary>
    public int CurrentCount { get; set; }

    /// <summary>将一个任务排到调度器上。</summary>
    /// <param name="task">要排队的任务。</param>
    protected sealed override void QueueTask(Task task)
    {
        // Add the task to the list of tasks to be processed. If there aren't enough
        // delegates currently queued or running to process tasks, schedule another.
        lock (_tasks)
        {
            //Console.WriteLine("Task Count : {0} ", _tasks.Count);
            _tasks.AddLast(task);
            if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
            {
                ++_delegatesQueuedOrRunning;
                NotifyThreadPoolOfPendingWork();
            }
        }
    }

    //private int executingCount = 0;
    //private static object executeLock = new object();

    /// <summary>
    /// 通知ThreadPool有工作要执行给这个调度器。
    /// </summary>
    private void NotifyThreadPoolOfPendingWork()
    {
        ThreadPool.UnsafeQueueUserWorkItem(_ =>
        {
            // Note that the current thread is now processing work items.
            // This is necessary to enable inlining of tasks into this thread.
            _currentThreadIsProcessingItems = true;
            try
            {
                // Process all available items in the queue.
                while (true)
                {
                    Task item;
                    lock (_tasks)
                    {
                        // When there are no more items to be processed,
                        // note that we're done processing, and get out.
                        if (_tasks.Count == 0)
                        {
                            --_delegatesQueuedOrRunning;

                            break;
                        }

                        // Get the next item from the queue
                        item = _tasks.First!.Value;
                        _tasks.RemoveFirst();
                    }

                    // Execute the task we pulled out of the queue
                    TryExecuteTask(item);
                }
            }
            // We're done processing items on the current thread
            finally { _currentThreadIsProcessingItems = false; }
        }, null);
    }

    /// <summary>试图在当前线程上执行指定的任务。</summary>
    /// <param name="task">要执行的任务。</param>
    /// <param name="taskWasPreviouslyQueued"></param>
    /// <returns>该任务是否可以在当前线程上执行。</returns>
    protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        // 如果这个线程还没有处理一个任务，我们就不支持内联。
        if (!_currentThreadIsProcessingItems) return false;

        // 如果该任务以前是排队的，就把它从队列中删除。
        if (taskWasPreviouslyQueued) TryDequeue(task);

        // 尝试运行该任务。
        return TryExecuteTask(task);
    }

    /// <summary>试图从调度器中删除一个先前安排的任务。</summary>
    /// <param name="task">要删除的任务。</param>
    /// <returns>是否能找到并删除该任务。</returns>
    protected sealed override bool TryDequeue(Task task)
    {
        lock (_tasks) return _tasks.Remove(task);
    }

    /// <summary>获取该调度器支持的最大并发级别。</summary>
    public sealed override int MaximumConcurrencyLevel
    { get { return _maxDegreeOfParallelism; } }

    /// <summary>获取当前在该调度器上调度的任务的枚举表。</summary>
    /// <returns>当前安排的任务的可枚举性。</returns>
    protected sealed override IEnumerable<Task> GetScheduledTasks()
    {
        bool lockTaken = false;
        try
        {
            Monitor.TryEnter(_tasks, ref lockTaken);
            if (lockTaken) return _tasks.ToArray();
            else throw new NotSupportedException();
        }
        finally
        {
            if (lockTaken) Monitor.Exit(_tasks);
        }
    }
}
